/*
 * HackSQL
 * Copyright (C) 2008-2009
 *
 * Authors: Felipe Nascimento Silva Pena
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <hacksql/hacksql.h>
#include <hacksql/hs_types.h>
#include <hacksql/hs_catalog.h>

/*
 * Returns the field type name
 */
static hs_string hs_type_name(hs_field_type type)
{
	switch (type) {
		case HS_FIELD_BLOB:
			return "blob";
		case HS_FIELD_BOOL:
			return "bool";
		case HS_FIELD_CHAR:
			return "char";
		case HS_FIELD_VARCHAR:
			return "varchar";
		case HS_FIELD_INTEGER:
			return "integer";
		default:
			return "unknown";
	}
}

static hs_string hs_constraint_type_name(hs_contraint_type type)
{
	switch (type) {
		case HS_COLUMN_PK:
			return "primary key";
		case HS_COLUMN_FK:
			return "foreing key";
		case HS_COLUMN_UNIQUE:
			return "unique";
		default:
			return "unknown";
	}
}

/*
 * Display the table structure
 */
static void hs_table_dump(hs_table *table)
{
	hs_uint num_fields = table->num_fields;
	hs_uint num_constraints = table->num_constraints;
	hs_field **field_p = table->fields;
	hs_contraint **constraint_p = table->constraints;
	
	printf("Table <%s> (fields: %d, constraints: %d) {\n", table->name, num_fields, table->num_constraints);
	
	if (field_p) {
		printf("  Fields {\n");
	
		while (num_fields--) {
			printf("    Field <name: %s, type: %s>\n", (*field_p)->name, hs_type_name((*field_p)->type));
			field_p++;
		}
		
		printf("  }\n");
	}
	
	if (constraint_p) {
		printf("\n  Constraints {\n");

		do {
			hs_field **constr_field_p = (*constraint_p)->fields;
			hs_uint num_constr_fields = (*constraint_p)->num_fields;
			
			printf("    Constraint <name: %s, type: %s, fields: %d>\n", (*constraint_p)->name,
				hs_constraint_type_name((*constraint_p)->type), (*constraint_p)->num_fields);

			do {
				printf("      Field <name: %s>\n", (*constr_field_p)->name);
				constr_field_p++;
			} while (--num_constr_fields);
			constraint_p++;
		} while (--num_constraints);
		
		printf("  }\n");
	}
	
	printf("}\n");
}

/*
 * Creates a new table
 */
static inline void hs_create_table(hs_schema *schema, hs_string name, hs_field **fields, hs_string comment)
{
	hs_table *table;
	
	table = malloc(sizeof(hs_table));
	table->name = name;
	table->fields = fields;
	table->num_fields = 0;
	table->create_time = table->update_time = time(NULL);
	table->version = HACKSQL_NUM_VERSION;
	table->comment = comment;
	table->constraints = NULL;
	table->num_constraints = 0;

	*(schema->tables + schema->num_tables) = table;
	schema->num_tables++;
}

/*
 * Creates a new field
 */
static inline void hs_add_field(hs_table *table, char *name, hs_field_type type)
{
	hs_uint name_len;
	hs_field *field;
	
	name_len = strlen(name);
	
	field = (hs_field *) malloc(sizeof(hs_field));
	field->name = strdup(name);
	field->name_len = name_len;
	field->type = type;
	
	*(table->fields + table->num_fields) = field;
	table->num_fields++;
}

static inline void hs_add_constraint(hs_table *table, hs_string name, hs_contraint_type type, hs_uint field_pos,
	hs_uint num_constraint, hs_uint num_field, hs_uint count)
{
	hs_contraint *constraint;
	hs_uint name_len;
	hs_short is_first = 1;
	
	name_len = strlen(name);
	
	if (table->constraints == NULL) {
		table->constraints = (hs_contraint **) calloc(1, sizeof(hs_contraint *));
	} else if (table->num_constraints < num_constraint+1) {
		table->constraints = realloc(table->constraints, (num_constraint+1) * sizeof(hs_contraint *));
		is_first = 1;
	} else if (table->num_constraints == num_constraint+1) {
		is_first = 0;
		constraint = *(table->constraints + num_constraint);
	}

	if (is_first) {
		constraint = (hs_contraint *) malloc(sizeof(hs_contraint));
		constraint->name = strdup(name);
		constraint->name_len = name_len;
		constraint->num_fields = 0;
		constraint->type = type;
	
		constraint->fields = (hs_field **) calloc(count, sizeof(hs_field *));
		*(table->constraints + num_constraint) = constraint;
		table->num_constraints++;		
	}

	*(constraint->fields + num_field) = *(table->fields + field_pos);
	constraint->num_fields++;
}

static inline void hs_create_schema(hs_catalog *catalog, hs_string name)
{
	hs_schema *schema;
	hs_uint name_len;
	
	name_len = strlen(name);
	
	schema = malloc(sizeof(hs_schema));
	schema->name = strdup(name);
	schema->name_len = name_len;
	
	*(catalog->schemas + catalog->num_schemas) = schema;	
	catalog->num_schemas++;
}

/*
 * Initializes a catalog
 */
static void hs_fill_structures(hs_catalog *catalog)
{
	hs_field **fields, **fields2;
	
	fields = (hs_field **) calloc(3, sizeof(hs_field *));
	
	catalog->schemas = (hs_schema **) calloc(1, sizeof(hs_schema *));
	catalog->num_schemas = 0;
	
	hs_create_schema(catalog, "schema_test");
	
	(*catalog->schemas)->tables = (hs_table **) calloc(2, sizeof(hs_table *));
	(*catalog->schemas)->num_tables = 0;
	
	hs_create_table(*(catalog->schemas), "test", fields, NULL);

	hs_add_field(*(*catalog->schemas)->tables, "foo", HS_FIELD_INTEGER);
	hs_add_field(*(*catalog->schemas)->tables, "bar", HS_FIELD_VARCHAR);
	hs_add_field(*(*catalog->schemas)->tables, "baz", HS_FIELD_VARCHAR);
	
	hs_add_constraint(*(*catalog->schemas)->tables, "TEST_PK_I", HS_COLUMN_PK, 0, 0, 0, 1);
	hs_add_constraint(*(*catalog->schemas)->tables, "TEST_UNIQUE_I", HS_COLUMN_UNIQUE, 1, 1, 0, 2);
	hs_add_constraint(*(*catalog->schemas)->tables, "TEST_UNIQUE_I", HS_COLUMN_UNIQUE, 2, 1, 1, 2);

	fields2 = (hs_field **) calloc(3, sizeof(hs_field *));
	
	hs_create_table(*(catalog->schemas), "test2", fields2, NULL);
	
	hs_add_field(*((*catalog->schemas)->tables+1), "f00", HS_FIELD_INTEGER);
}

static void hs_destroy_schemas(hs_schema *schema)
{
	hs_table **table_p = schema->tables;
	hs_uint num_tables = schema->num_tables;
	
	while (num_tables--) {
		hs_field **field_p = (*table_p)->fields;
		hs_contraint **constraint_p = (*table_p)->constraints;
		hs_uint num_fields = (*table_p)->num_fields;
		hs_uint num_constraints = (*table_p)->num_constraints;
		
		if (num_fields) {
			HS_DEBUG("Freeing fields...\n");
			while (num_fields--) {
				free((*field_p)->name);
				free(*field_p);
				field_p++;
			}
			free((*table_p)->fields);
		}
		
		if (num_constraints) {
			HS_DEBUG("Freeing constraints...\n");
			while (num_constraints--) {
				free((*constraint_p)->name);
				free((*constraint_p)->fields);
				free(*constraint_p);
				constraint_p++;
			}
			free((*table_p)->constraints);
		}
		HS_DEBUG("Freeing table...\n");
		free(*table_p);
		table_p++;
	}
}

static void hs_destroy_catalog(hs_catalog *catalog)
{
	hs_destroy_schemas(*catalog->schemas);
	
	HS_DEBUG("Freeing catalog...\n");

	HS_SECURE_FREE(catalog->schemas);
	HS_SECURE_FREE(catalog->users);
	HS_SECURE_FREE(catalog->roles);
}

/*
int main(int argc, char **argv)
{
	hs_catalog catalog;
	hs_table **table_p;
	hs_uint num_tables;
	
	catalog.users = NULL;
	catalog.roles = NULL;
	catalog.schemas = NULL;
	hs_fill_structures(&catalog);
	
	table_p = (*catalog.schemas)->tables;
	num_tables = (*catalog.schemas)->num_tables;
	
	while (num_tables--) {
		hs_table_dump(*table_p++);
	}

	hs_destroy_catalog(&catalog);
	
	return 0;
}
*/
